package apidocs;

import org.apache.commons.lang.ClassUtils;
import org.apache.commons.lang.StringUtils;
import play.mvc.Router;
import play.templates.JavaExtensions;
import play.vfs.VirtualFile;

import java.lang.reflect.*;
import java.util.*;

/**
 * 文档生成页面增强工具集
 * Created by zhangzc on 2/17/17.
 */
public class XJavaExtensionForApi extends JavaExtensions {

    public static String getApiName(Class clazz) {
        ApiService apiService = (ApiService) clazz.getAnnotation(ApiService.class);
        return apiService.value();
    }

    public static ApiService getApiService(Class clazz) {
        ApiService apiService = (ApiService) clazz.getAnnotation(ApiService.class);
        return apiService;
    }

    public static String getApiActionName(Method method) {
        Api api = (Api) method.getAnnotation(Api.class);
        return api.name();
    }

    public static Api getApiAction(Method method) {
        Api api = (Api) method.getAnnotation(Api.class);
        return api;
    }

    public static Parameter[] getApiActionParam(Method method) {
        return method.getParameters();
    }

    public static ApiParam getApiActionParamAnnotation(Parameter param) {
        return param.getAnnotation(ApiParam.class);
    }

    public static String getActionAsUrl(Method method, Class clazz) {
        return Router.getFullUrl(clazz.getCanonicalName() + "." + method.getName());
    }

    public static String getActionAsSubUrl(Method method, Class clazz) {
        return Router.reverse(clazz.getCanonicalName() + "." + method.getName()).url;
    }

    public static Boolean isApiResultPrimitive(Class clazz) {
        boolean isPrimitiveOrWrapped =
                clazz.isPrimitive() || ClassUtils.wrapperToPrimitive(clazz) != null
                || String.class.equals(clazz);
        return isPrimitiveOrWrapped;
    }

    public static String getApiResultPrimitiveName(Class clazz) {
        if(String.class.equals(clazz)){
            return "String";
        }
        return ClassUtils.wrapperToPrimitive(clazz).getName();
    }

    public static List<Method> getApiActions(Class clazz) {
        List<Method> result = new ArrayList();
        Method[] methods = clazz.getMethods();
        for (Method m : methods) {
            if (m.isAnnotationPresent(Api.class)) {
                result.add(m);
            }
        }
        Collections.sort(result, new Comparator<Method>() {
            @Override
            public int compare(Method o1, Method o2) {
                Api a1 = (Api) o1.getAnnotation(Api.class);
                Api a2 = (Api) o2.getAnnotation(Api.class);
                if(a1.weight() == a2.weight()){
                    return 0;
                }
                return a1.weight() > a2.weight() ? -1 : 1;
            }
        });
        return result;
    }

    public static List<Field> getApiResultField(Class clazz) {
        List<Field> result = new ArrayList();
        Field[] fields = clazz.getDeclaredFields();
        for (Field m : fields) {
            result.add(m);
        }
        return result;
    }

    public static ApiResult getApiResult(Class clazz) {
        ApiResult api = (ApiResult) clazz.getAnnotation(ApiResult.class);
        return api;
    }

    public static ApiResultField getApiResultField(Field field) {
        ApiResultField api = (ApiResultField) field.getAnnotation(ApiResultField.class);
        return api;
    }

    public static String getFieldSimpleName(Field field) {
        Class type = field.getType();
        if (type.equals(int.class) || type.equals(Integer.class)) {
            return "Integer";
        }

        if (type.equals(long.class) || type.equals(Long.class)) {
            return "Long";
        }

        if (type.equals(double.class) || type.equals(Double.class)) {
            return "Double";
        }

        if (type.equals(float.class) || type.equals(Float.class)) {
            return "Float";
        }

        if (type.equals(boolean.class) || type.equals(Boolean.class)) {
            return "Boolean";
        }

        if (type.equals(String.class)) {
            return "String";
        }

        if (type.equals(List.class)) {
            return "List";
        }

        if (type.equals(Set.class)) {
            return "Set";
        }

        if (type.equals(Map.class)) {
            return "Map";
        }

        if (type.equals(Object.class)) {
            return "Object";
        }

        return type.getName();
    }

    public static Boolean isParameterizedField(Field field) {
        Class fieldClazz = field.getType(); // 得到field的class及类型全路径

        if (fieldClazz.isPrimitive()) return false;  //【1】 //判断是否为基本类型

        if (fieldClazz.getName().startsWith("java.lang")) return false; ////getName()返回field的类型全路径；

        Type fc = field.getGenericType(); // 关键的地方，如果是List类型，得到其Generic的类型

        if (fc == null) return false;

        if (fc instanceof ParameterizedType) { // 【3】如果是泛型参数的类型

            ParameterizedType pt = (ParameterizedType) fc;

            Class genericClazz = (Class) pt.getActualTypeArguments()[0]; //【4】 得到泛型里的class类型对象。
            return true;

        }

        return true;
    }

    public static int getParameterizedFieldType(Field field) {
        Class fieldClazz = field.getType(); // 得到field的class及类型全路径

        if (fieldClazz.isPrimitive()) return 0;  //【1】 //判断是否为基本类型

        if (fieldClazz.getName().startsWith("java.lang")) return 1; ////getName()返回field的类型全路径；

        Type fc = field.getGenericType(); // 关键的地方，如果是List类型，得到其Generic的类型

        if (fc != null && fc instanceof ParameterizedType) { // 【3】如果是泛型参数的类型

            ParameterizedType pt = (ParameterizedType) fc;

            Class genericClazz = (Class) pt.getActualTypeArguments()[0]; //【4】 得到泛型里的class类型对象。

            if (genericClazz.isPrimitive()|| genericClazz.getName().startsWith("java.lang")) return 4; //判断是否为基本类型

            return 3;

        }

        return 2;
    }

    public static Class getParameterizedField(Field field) {
        Class fieldClazz = field.getType(); // 得到field的class及类型全路径

        if (fieldClazz.isPrimitive()) return null;  //【1】 //判断是否为基本类型

        if (fieldClazz.getName().startsWith("java.lang")) return null; ////getName()返回field的类型全路径；

        Type fc = field.getGenericType(); // 关键的地方，如果是List类型，得到其Generic的类型

        if (fc != null && fc instanceof ParameterizedType) { // 【3】如果是泛型参数的类型

            ParameterizedType pt = (ParameterizedType) fc;

            Class genericClazz = (Class) pt.getActualTypeArguments()[0]; //【4】 得到泛型里的class类型对象。
            return genericClazz;

        }

        return fieldClazz;
    }

    public static Boolean isParameterized(Parameter field) {
        Class fieldClazz = field.getType(); // 得到field的class及类型全路径

        if (fieldClazz.isPrimitive()) return false;  //【1】 //判断是否为基本类型

        if (fieldClazz.getName().startsWith("java.lang")) return false; ////getName()返回field的类型全路径；

        Type fc = field.getParameterizedType(); // 关键的地方，如果是List类型，得到其Generic的类型

        if (fc == null) return false;

        if(((Class)fc).isArray()){
            return true;
        }

        if (fc instanceof ParameterizedType) { // 【3】如果是泛型参数的类型

            ParameterizedType pt = (ParameterizedType) fc;

            Class genericClazz = (Class) pt.getActualTypeArguments()[0]; //【4】 得到泛型里的class类型对象。
            return true;

        }

        return false;
    }

    public static Class getParameterized(Parameter field) {
        Class fieldClazz = field.getType(); // 得到field的class及类型全路径

        if (fieldClazz.isPrimitive()) return null;  //【1】 //判断是否为基本类型

        if (fieldClazz.getName().startsWith("java.lang")) return null; ////getName()返回field的类型全路径；

        Type fc = field.getParameterizedType(); // 关键的地方，如果是List类型，得到其Generic的类型

        if(((Class)fc).isArray()){
            return ((Class) fc).getComponentType();
        }

        if (fc != null && fc instanceof ParameterizedType) { // 【3】如果是泛型参数的类型

            ParameterizedType pt = (ParameterizedType) fc;

            Class genericClazz = (Class) pt.getActualTypeArguments()[0]; //【4】 得到泛型里的class类型对象。
            return genericClazz;

        }

        return fieldClazz;
    }

    public static List<VirtualFile> listMarkdownFiles(VirtualFile docDir) {
        if (!docDir.exists()) {
            return Collections.emptyList();
        }
        List<VirtualFile> files = new ArrayList();
        docDir.list().forEach(f -> {
            if (StringUtils.endsWith(f.getName(), ".md")) {
                files.add(f);
            }
        });
        return files;
    }

    public static String renderAsTemplate(String template) {
        return TemplateUtil.renderAsGroovyTemplate(template, new HashMap());
    }

    public static String loadTitle(VirtualFile file) {
        String content = file.contentAsString();
        Scanner scanner = new Scanner(content);
        while (scanner.hasNextLine()) {
            String line = scanner.nextLine();
            if (StringUtils.startsWith(line, "# ")) {
                return StringUtils.trimToEmpty(StringUtils.substringAfter(line, "#"));
            }
        }
        return file.getName();
    }
}
